package com.atguigu.zy.interview.juc.collectiones;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @Author : Jungle
 * @Description :  集合类不安全的问题
 *                  并发修改异常   java.util.ConcurrentModificationException
 */
public class ContainerNotSafeDemo {
    public static void main(String[] args) {
        /**
         * 1、hashmap和hashtable区别
             * 1、安全性而言，  hashtable put。get都加synchronize 锁了
             * 2、继承不同  HashMap==>AbstractMap    hashtable=> Dictionary
         *     3、hashtable  key和null不能为空(看高亮的源码) ,hashmap put方法中 寻址方法hash(key)，key为null返回为0
         *     4、扩容 addEntry =>rehash  移位运算 左移1位 2n+1    hashMap resize 左移一位2n
         *     5、ConcurrentHashMap采用CAS和synchronized来保证并发安全
         *
         *      hashtable put方法：
         *       public synchronized V put(K key, V value) {
         *         // Make sure the value is not null
         *        todo if (value == null) {
         *             throw new NullPointerException();
         *         }
         *
         *         // Makes sure the key is not already in the hashtable.
         *         Entry<?,?> tab[] = table;
         *        todo int hash = key.hashCode();
         *
         *         int index = (hash & 0x7FFFFFFF) % tab.length;
         *         @SuppressWarnings("unchecked")
         *         Entry<K, V> entry = (Entry<K,V>)tab[index];
         *         for(; entry != null ; entry = entry.next) {
         *             if ((entry.hash == hash) && entry.key.equals(key)) {
         *                 V old = entry.value;
         *                 entry.value = value;
         *                 return old;
         *             }
         *         }
         */
        mapNotSafe();

        //setNotSafe();

    }

    private static void mapNotSafe() {
        //Map<String, String> map = new HashMap<>();
        //Map<String, String> map = new Hashtable<>();
        //Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 1; i <= 100; i++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }

    private static void setNotSafe() {
        Set<String> set = new HashSet<>();
        //Set<String> set = Collections.synchronizedSet(new HashSet<>());
        //Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 1; i <= 30; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
        /**
         * 1、HashSet底层是hashMap，只不过，value值默认给了一个PRESENT的一个常量
         */
        //new HashSet<>().add("a");
    }


    private static void listNotsafe() {
        /**
         * 1、线程不安全 ，add方法没加锁  Vector加锁，但是性能太低
         *   Vector jdk1.1出来了，  ArrayList 1.2出来的
         */
        //List<String> list = new ArrayList<>();
        //List<String> list = new Vector<>();
        //List<String> list = Collections.synchronizedList(new ArrayList<>());
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 1; i <= 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
        /**
         * 1、故障现象
         *      java.util.ConcurrentModificationException
         * 2、导致原因
         *      并发争抢修改导致，参考我们花名册签名情况
         *      一个人正在写入，另外一个同学过来抢夺，导致并发修改异常
         *
         * 3、解决方案
         *   3.1  new Vector<>();
         *          add方法没加锁  Vector加锁，但是性能太低
         *          Vector jdk1.1出来了，  ArrayList 1.2出来的，可以看出ArrayList 不考虑安全的情况下使用
         *   3.2  Collections.synchronizedList(new ArrayList<>());  //添加同步代码块
         *   3.3  new CopyOnWriteArrayList<>();
         * 笔记：
         * 写时复制
         *  CopyOnwrite容器即写时复制的容器。往一个容器添加元素的时候，不直接往当前容器object[]添加，而是先将当前容器object[]进行copy.
         * 复制出一个新的容器object[ ] newELements，然后新的容器object[] newElements里添加元素，添加完元素之后，
         * 再将原容器的引用指向新的容器setArray(newELements);。这样做的好处是可以对CopyOnwrite容器进行并发的读，
         * 而不需要加锁，因为当前容器不会添加任何元素。所以CopyOnwrite容器也是一种读写分离的思想，读和写不同的容器
         *
         * 而且数组用 volatile修饰保证可见性
         * private transient volatile Object[] array;
         *
         *     public boolean add(E e) {
         *         final ReentrantLock lock = this.lock;
         *         lock.lock();
         *         try {
         *             Object[] elements = getArray();
         *             int len = elements.length;
         *             Object[] newElements = Arrays.copyOf(elements, len + 1);
         *             newElements[len] = e;
         *             setArray(newElements);
         *             return true;
         *         } finally {
         *             lock.unlock();
         *         }
         *     }
         *
         * 4、优化建议（同样的错误不犯第二次）
         */}
}
